using System;
using System.Collections.Generic;
using System.Web.Security;
using System.Threading;
using System.Security.Principal;
using System.ServiceModel;

namespace SecurityAgent
{

    /// <summary>
    /// membership provider using Bo's Security Agent service
    /// </summary>
    public class SecurityAgentMembershipProvider : MembershipProvider
    {
        private string _applicationName;
        private string _applicationKey;

        private string _serviceEndPoint;

        private MembershipProviderServiceReference.MembershipProviderServiceClient _serviceClient;

        public MembershipProviderServiceReference.MembershipProviderServiceClient ServiceClient
        {
            get
            {
                if (_serviceClient == null)
                {
                    if (String.IsNullOrEmpty(_serviceEndPoint))
                        _serviceClient = new MembershipProviderServiceReference.MembershipProviderServiceClient();
                    else
                        _serviceClient = new MembershipProviderServiceReference.MembershipProviderServiceClient(_serviceEndPoint);

                }

                return _serviceClient;
            }
        }


        private SASIdentity _sasIdentity;

        public string CurrentUserToken
        {
            get
            {
                if (_sasIdentity == null)
                {
                    _sasIdentity = (SASIdentity)Thread.CurrentPrincipal.Identity;
                }

                if (_sasIdentity == null)
                    return string.Empty;
                else
                    return _sasIdentity.UserToken;
            }
        }



        public SecurityAgentMembershipProvider()
        {

        }



        /// <summary>
        /// Initialize the provider. 
        /// </summary>
        /// <remarks>Part of the initialization process includes establishing a session with the remote provider for security.</remarks>
        /// <param name="name"></param>
        /// <param name="config"></param>
        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);

            _applicationKey = this.GetConfigValue(config["applicationKey"], "");

            if (_applicationKey == null)
            {
                throw new Exception("Application Key is not configured");
            }

            _serviceEndPoint = this.GetConfigValue(config["serviceEndPoint"], "");
        }

        private string GetConfigValue(string configValue, string defaultValue)
        {
            if (String.IsNullOrEmpty(configValue))
                return defaultValue;

            return configValue;
        }

        public string ApplicationKey
        {
            get
            {
                return _applicationKey;
            }
        }

        public override string ApplicationName
        {
            get
            {
                return _applicationName;
            }
            set
            {
                _applicationName = value;
            }
        }

        public override bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            try
            {
                return ServiceClient.ChangePassword(_applicationKey, username, oldPassword, newPassword);
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
        {
            return ServiceClient.ChangePasswordQuestionAndAnswer(_applicationKey, username, password, newPasswordQuestion, newPasswordAnswer);
        }

        protected MembershipUser ConvertUser(SecurityPrincipal user)
        {
            if (user == null) return null;
            System.Web.Security.MembershipUser membershipUser =
              new System.Web.Security.MembershipUser("SecurityAgentMembershipProvider",
                user.UserName,
                user.ProviderUserKey,
                user.Email,
                user.PasswordQuestion,
                user.Comment,
                user.IsApproved,
                user.IsLockedOut,
                user.CreationDate,
                user.LastLoginDate,
                user.LastActivityDate,
                user.LastPasswordChangedDate,
                user.LastLockoutDate);
            return membershipUser;

        }

        protected SecurityPrincipal ConvertUser(System.Web.Security.MembershipUser user)
        {
            if (user == null) return null;
            SecurityPrincipal membershipUser = new SecurityPrincipal();
            membershipUser.Comment = user.Comment;
            membershipUser.CreationDate = user.CreationDate;
            membershipUser.Email = user.Email;
            membershipUser.IsApproved = user.IsApproved;
            membershipUser.IsLockedOut = user.IsLockedOut;
            membershipUser.IsOnline = user.IsOnline;
            membershipUser.LastActivityDate = user.LastActivityDate;
            membershipUser.LastLockoutDate = user.LastLockoutDate;
            membershipUser.LastLoginDate = user.LastLoginDate;
            membershipUser.LastPasswordChangedDate = user.LastPasswordChangedDate;
            membershipUser.PasswordQuestion = user.PasswordQuestion;
            membershipUser.ProviderUserKey = user.ProviderUserKey;
            membershipUser.UserName = user.UserName;
            return membershipUser;
        }

        public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out System.Web.Security.MembershipCreateStatus status)
        {
            try
            {
                SecurityPrincipal ret = ServiceClient.CreateUser(out status, _applicationKey, username, password, email, passwordQuestion, passwordAnswer, isApproved, Guid.NewGuid());
                return ConvertUser(ret);
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override bool DeleteUser(string username, bool deleteAllRelatedData)
        {
            try
            {
                return ServiceClient.DeleteUser(_applicationKey, CurrentUserToken, username, deleteAllRelatedData);
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override bool EnablePasswordReset
        {
            get
            {
                try
                {
                    return ServiceClient.GetEnablePasswordReset(_applicationKey);
                }
                catch (FaultException<UnauthorizedAccessFault> e1)
                {
                    throw new UnauthorizedAccessException(e1.Detail);
                }
                catch (FaultException<SecurityFault> e2)
                {
                    throw new GeneralSecurityException(e2.Detail);
                }
                catch (FaultException e3)
                {
                    throw new Exception(e3.Message + ":" + e3.Reason);
                }
            }
        }

        public override bool EnablePasswordRetrieval
        {
            get
            {
                try
                {
                    return ServiceClient.GetEnablePasswordRetrieval(_applicationKey);
                }
                catch (FaultException<UnauthorizedAccessFault> e1)
                {
                    throw new UnauthorizedAccessException(e1.Detail);
                }
                catch (FaultException<SecurityFault> e2)
                {
                    throw new GeneralSecurityException(e2.Detail);
                }
                catch (FaultException e3)
                {
                    throw new Exception(e3.Message + ":" + e3.Reason);
                }
            }
        }

        public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            try
            {
                MembershipProviderServiceReference.SecurityPrincipalCollection spc = ServiceClient.FindUsersByEmail(out totalRecords, _applicationKey, emailToMatch, pageIndex, pageSize);

                if (spc == null) return null;

                MembershipUserCollection muc = new MembershipUserCollection();

                foreach (SecurityPrincipal sp in spc)
                {
                    muc.Add(ConvertUser(sp));
                }

                return muc;
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
        {
            try
            {
                MembershipProviderServiceReference.SecurityPrincipalCollection spc = ServiceClient.FindUsersByName(out totalRecords, _applicationKey, usernameToMatch, pageIndex, pageSize);

                if (spc == null) return null;

                MembershipUserCollection muc = new MembershipUserCollection();

                foreach (SecurityPrincipal sp in spc)
                {
                    muc.Add(ConvertUser(sp));
                }

                return muc;
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
        {
            try
            {
                MembershipProviderServiceReference.SecurityPrincipalCollection spc = ServiceClient.GetAllUsers(out totalRecords, _applicationKey, CurrentUserToken, pageIndex, pageSize);

                if (spc == null) return null;

                MembershipUserCollection muc = new MembershipUserCollection();

                foreach (SecurityPrincipal sp in spc)
                {
                    muc.Add(ConvertUser(sp));
                }

                return muc;
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override int GetNumberOfUsersOnline()
        {
            try
            {
                return ServiceClient.GetNumberOfUsersOnline(_applicationKey);
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override string GetPassword(string username, string answer)
        {
            try
            {
                return ServiceClient.GetPassword(_applicationKey, username, answer);
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override System.Web.Security.MembershipUser GetUser(string username, bool userIsOnline)
        {
            try
            {
                return ConvertUser(ServiceClient.GetUserByUserName(_applicationKey, username, userIsOnline));
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override System.Web.Security.MembershipUser GetUser(object providerUserKey, bool userIsOnline)
        {
            try
            {
                return ConvertUser(ServiceClient.GetUserByKey(_applicationKey, providerUserKey, userIsOnline));
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override string GetUserNameByEmail(string email)
        {
            try
            {
                return ServiceClient.GetUserNameByEmail(_applicationKey, email);
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }

        }

        public override int MaxInvalidPasswordAttempts
        {
            get
            {
                return ServiceClient.GetMaxInvalidPasswordAttempt(_applicationKey);
            }
        }

        public override int MinRequiredNonAlphanumericCharacters
        {
            get
            {
                return ServiceClient.GetMinRequiredPasswordLength(_applicationKey);
            }
        }

        public override int MinRequiredPasswordLength
        {
            get
            {
                return ServiceClient.GetMinRequiredPasswordLength(_applicationKey);
            }
        }

        public override int PasswordAttemptWindow
        {
            get
            {
                return ServiceClient.GetPasswordAttemptWindow(_applicationKey);
            }
        }

        public override MembershipPasswordFormat PasswordFormat
        {
            get
            {
                return (MembershipPasswordFormat)ServiceClient.GetPasswordFormat(_applicationKey);
            }
        }



        public override string PasswordStrengthRegularExpression
        {
            get
            {
                return ServiceClient.GetPasswordStrengthRegularExpression(_applicationKey);
            }
        }

        public override bool RequiresQuestionAndAnswer
        {
            get
            {
                return ServiceClient.GetRequiresQuestionAndAnswer(_applicationKey);
            }
        }

        public override bool RequiresUniqueEmail
        {
            get
            {
                return ServiceClient.GetRequiresUniqueEmail(_applicationKey);
            }
        }

        public override string ResetPassword(string username, string answer)
        {
            try
            {
                return ServiceClient.ResetPassword(_applicationKey, username, answer);
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override bool UnlockUser(string userName)
        {
            try
            {
                return ServiceClient.UnlockUser(_applicationKey, CurrentUserToken, userName);
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override void UpdateUser(System.Web.Security.MembershipUser user)
        {
            try
            {
                SecurityPrincipal prin = ConvertUser(user);
                if (user != null)
                    ServiceClient.UpdateUser(_applicationKey, CurrentUserToken, user.UserName, prin);
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public override bool ValidateUser(string username, string password)
        {
            try
            {
                return ServiceClient.ValidateUser(_applicationKey, username, password);
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        public string ValidateAndRegisterUser(string username, string password)
        {
            try
            {
                return ServiceClient.ValidateAndRegisterUser(_applicationKey, username, password);
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

        //public bool GenerateSession(string username, string password)
        //{
        //    //generate server key, kick it back to local memory, and perform login as normal. 
        //    MembershipService svc = GetServiceInstance();
        //    ServiceResponse resp = svc.GenerateSession(username, password);
        //    if (resp.Result == eResult.Success)
        //    {
        //        SecurityContext.CurrentKey = resp.Data.ToString();
        //        return true;
        //    }
        //    else if (resp.Result == eResult.InvalidRegistration)
        //        return false;
        //    else
        //        throw new ServiceException(resp.Message);
        //}

        public void ExpireToken(string userToken)
        {
            try
            {
                if (!string.IsNullOrEmpty(userToken))
                {
                    ServiceClient.ExpireToken(_applicationKey, userToken);
                }
            }
            catch (FaultException<UnauthorizedAccessFault> e1)
            {
                throw new UnauthorizedAccessException(e1.Detail);
            }
            catch (FaultException<SecurityFault> e2)
            {
                throw new GeneralSecurityException(e2.Detail);
            }
            catch (FaultException e3)
            {
                throw new Exception(e3.Message + ":" + e3.Reason);
            }
        }

    }



}
